package w.z.x.framework.repository.wrappers;

import w.z.x.framework.repository.interfaces.Compare;
import w.z.x.framework.repository.interfaces.Nested;
import w.z.x.framework.repository.interfaces.SerialFunction;
import w.z.x.framework.repository.interfaces.Wrapper;
import w.z.x.framework.repository.structures.Segment;

import java.util.Collection;
import java.util.function.Consumer;

/**
 * 抽象条件封装
 *
 * @author 魏祖潇
 */
public class AbstractWrapper<T, R, Child extends AbstractWrapper<T, R, Child>>
        implements Nested<Child, Child>, Compare<Child, R>, Wrapper<T> {

    protected final Segment where = Segment.build(null, "and", null);
    protected final Segment[] index = new Segment[]{where};

    protected final int[] offset = {0};
    protected final int[] limit = {0};

    /**
     * 添加语句片段
     *
     * @param segment 语句片段
     */
    protected final void add(Segment segment) {
        if (this.index[0] == this.where || this.where.getChildren().isEmpty()) {
            this.where.append(segment);
        } else {
            this.index[0].append(segment);
        }
    }

    /**
     * 添加
     *
     * @param r     字段
     * @param value 值
     * @param op    操作
     */
    protected void add(R r, Object value, String op) {
        if (r instanceof String) {
            this.add(Segment.build(r.toString(), op, value));
        } else {
            this.add(Segment.build(SerialFunction.field((SerialFunction<T, ?>) r), op, value));
        }
    }

    @Override
    public Child eq(boolean condition, R r, Object value) {
        if (condition) {
            add(r, value, "=");
        }
        return (Child) this;
    }

    @Override
    public Child ne(boolean condition, R r, Object value) {
        if (condition) {
            add(r, value, "<>");
        }
        return (Child) this;
    }

    @Override
    public Child gt(boolean condition, R r, Object value) {
        if (condition) {
            add(r, value, ">");
        }
        return (Child) this;
    }

    @Override
    public Child lt(boolean condition, R r, Object value) {
        if (condition) {
            add(r, value, "<");
        }
        return (Child) this;
    }

    @Override
    public Child ge(boolean condition, R r, Object value) {
        if (condition) {
            add(r, value, ">=");
        }
        return (Child) this;
    }

    @Override
    public Child le(boolean condition, R r, Object value) {
        if (condition) {
            add(r, value, "<=");
        }
        return (Child) this;
    }

    @Override
    public Child like(boolean condition, R r, Object value) {
        if (condition) {
            add(r, value, "like");
        }
        return (Child) this;
    }

    @Override
    public Child notLike(boolean condition, R r, Object value) {
        if (condition) {
            add(r, value, "not like");
        }
        return (Child) this;
    }

    @Override
    public Child in(boolean condition, R r, Collection<?> values) {
        if (condition) {
            add(r, values, "in");
        }
        return (Child) this;
    }

    @Override
    public Child notIn(boolean condition, R r, Collection<?> values) {
        if (condition) {
            add(r, values, "not in");
        }
        return (Child) this;
    }

    @Override
    public Child isNull(boolean condition, R r) {
        if (condition) {
            add(r, null, "is null");
        }
        return (Child) this;
    }

    @Override
    public Child notNull(boolean condition, R r) {
        if (condition) {
            add(r, null, "is not null");
        }
        return (Child) this;
    }

    @Override
    public Child and(boolean condition, Consumer<Child> consumer) {
        if (condition) {
            Segment segment = Segment.build(null, "and", null);
            add(segment);
            Segment grand = this.index[0];
            this.index[0] = segment;
            consumer.accept((Child) this);
            this.index[0] = grand;
        }
        return (Child) this;
    }

    @Override
    public Child or() {
        Segment segment = Segment.build(null, "or", null);
        add(segment);
        return (Child) this;
    }

    @Override
    public Segment whereSegment() {
        return this.where;
    }

    @Override
    public int offset() {
        return this.offset[0];
    }

    @Override
    public int limit() {
        return this.limit[0];
    }

    @Override
    public Wrapper<T> offset(int offset) {
        this.offset[0] = offset;
        return this;
    }

    @Override
    public Wrapper<T> limit(int limit) {
        this.limit[0] = limit;
        return this;
    }

}
